iT邦幫忙

2023 iThome 鐵人賽

DAY 22
1
Modern Web

就是個Go,我也可以啦!GOGO系列 第 22

2023鐵人賽Day 22 Go X html template

  • 分享至 

  • xImage
  •  

template

html/template 簡介

什麼是 html/template?

在完整的後端架構中,MVC 是一常見的設計模式。當 View 可以被框架內建,例如 Ruby on Rails 的 ERB 可以渲染 HTML,Go 則提供了 html/template。

html/template 的特點

  • 基於數據驅動的模板包裝,用於生成安全的HTML。
  • 為了確保輸出的 HTML 安全性,推薦使用 html/template。

html/template 與 text/template 的關聯

html/template 對 text/template 進行了封裝,賦予其特定功能,以確保可以安全地使用模板。

tmpl, err := template.New("name").Parse(...)
// Error checking elided
err = tmpl.Execute(out, data)

使用這段程式碼,你可以創建並執行一個新的模板。

安全性考量

  • 動態生成的內容,如 HTML,有時可能會受到不安全的惡意程式攻擊。但使用 html/template,即使面對惡意的輸入,生成的 HTML 也會保持安全,不執行任何危險的代碼。
  • 當在HTML插入文字時,需要確保被會被誤解為代碼,所以需要“轉譯”,例如 "<"應該被轉譯為"&lt",這樣就不會被解讀為HTML的標籤

安全模型

  • 安全模型假設:模板的作者是值得信賴的,而執行模板時傳遞給Execute函式中的data參數可能不是安全的,所以模板數入的數據如果是有害的,該包裝也會確保輸出是安全的

應用

我們來寫第一個範本吧
由於需要顯示在網頁上,所以我們也會需要用到上一個章節http的知識
故我們先把http及server架起來吧

package main

import (
	"fmt"
	"net/http"
)

func handlePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World")
}

func main() {
	server := &http.Server{
		Addr: ":8080",
	}
	http.HandleFunc("/", handlePage)
	server.ListenAndServe()
}

架起來後我們再來把template加上去

package main

import (
	"fmt"
	"net/http"
)

func handlePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello World")
  
//   ADD HERE
  
  tmpl, err := template.New("index").Parse(`<h1>{{.Title}}</h1>`)
  
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }
  
}

func main() {
	server := &http.Server{
		Addr: ":8080",
	}
	http.HandleFunc("/", handlePage)
	server.ListenAndServe()
}

接下來,我們要來注入資料結構

package main

import (
  //Add here
	"html/template"
	"net/http"
)

type PageVariables struct {
	Title string
}

func handlePage(w http.ResponseWriter, r *http.Request) {
	tmpl, err := template.New("index").Parse(`<h1>{{.Title}}</h1>`)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	//   Add here
	pageVariables := PageVariables{
		Title: "Welcome to our website!",
	}

	err = tmpl.Execute(w, pageVariables)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

func main() {
	server := &http.Server{
		Addr: ":8080",
	}
	http.HandleFunc("/", handlePage)
	server.ListenAndServe()
}

語法說明

範本語法會包含在 "{{" 和 "}}"中間,其中"{{.}}"中的典表示當前物件,再傳入結構物件時,可以根據"."來存取對應的欄位,以上個例子為例,我有一個結構物件名為"pageVariables"

type PageVariables struct {
	Title string
}

pageVariables := PageVariables{
		Title: "Welcome to our website!",
	}

而我的樣本語法為

tmpl, err := template.New("index").Parse(`<h1>{{.Title}}</h1>`)

故,{.Title}就會被pageVariables的Title欄位替換

pipeline 管道

在go模板語法中, pipeline是一種傳遞數據的機制,不一定要使用|,只需要將一個表達式的結果傳遞給另一個表達式,直到最後的輸出,都可以稱為pipeline

在go的template語法裡,可以用以下pipeline的方式進行

  • 基本的pipeline: 為表達數據的一個表達式,當範本在處理一個數據結構時
    • 範本語法{{.FieldName}},範本將會將數據結構中的FieldName傳入內
  • 使用函數:可以將pipeline的結果傳遞給函數或方法
    • 範本語法: {{ToLower .FieldName}}首先從某個對象中取得.FieldName的值,然後傳遞給ToLower這個函數
  • 鏈式操作:你可以將多個操作鏈再一起
    • 範本語法:{{.Values | len}
  • 默認值:
    • 範本語法:{{.Field | default "No value"}}

條件判斷

在 Go 的 text/template 和 html/template 套件中,if 並不支援傳統的比較運算符如 <、>、== 等。這是為了確保模板的語法簡單和一致。

為了解決這個限制,Go 提供了一些特定的函數來完成這些比較操作。這些函數包括:

  • eq: 等於
  • ne: 不等於
  • lt: 小於
  • le: 小於或等於
  • gt: 大於
  • ge: 大於或等於

我們來看以下範例

type User struct {
	Age  int
	Name string
}

func handlePage(w http.ResponseWriter, r *http.Request) {

	tmpl, err := template.New("index").Parse(`<h1>年紀</h1>
	{{if lt .Age 18}}
		<p>{{.Name}}, 你还未成年!</p>
	{{else if lt .Age 60}}
		<p>{{.Name}}, 你是一位成年人。</p>
	{{else}}
		<p>{{.Name}}, 你是一位长者。</p>
	{{end}}`)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	u := User{Age: 15, Name: "Tom"}

	err = tmpl.Execute(w, u)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

迴圈 range

func handlePage(w http.ResponseWriter, r *http.Request) {

	people := map[string]int{
		"Alice":   30,
		"Bob":     25,
		"Charlie": 35,
	}

	tmpl, err := template.New("index").Parse(`
	<ul>
	{{range $key, $value := .}}
          <li>{{$key}}: {{$value}} years old</li>
	{{end}}
	</ul>
	`)

	if err != nil {

		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	err = tmpl.Execute(w, people)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

}

func main() {
	server := &http.Server{
		Addr: ":8080",
	}
	http.HandleFunc("/", handlePage)
	server.ListenAndServe()
}


上一篇
2023鐵人賽Day 21 Go X Http
下一篇
2023鐵人賽Day 23 Go X web 伺服器
系列文
就是個Go,我也可以啦!GOGO30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言